<template name="gesture-password">
	<view ref="gestureArea" class="gesture-box" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend">
		<view class="points">
			<view class="item" v-for="(item, index) in items" :key="index" :class="{ active: item.active }">
				<view class="dot-outer">
					<view class="dot"></view>
				</view>
			</view>
		</view>

		<view class="line-segment" v-for="(line,index) in lineSegment" :key="index" :style="{
			top: line.top *2 + 'rpx',
			left: line.left *2+ 'rpx',
			width: line.width*2 + 'rpx',
			transform: 'rotate(' + line.angle + 'deg)'
		}"></view>

	</view>
</template>

<script>
export default {
	name: "gesture-password",
	props: {

		// 是否启用
		isUse: {
			type: Boolean,
			default: true
		},// 校验点数量
		dotVerifyNum: {
			type: Boolean,
			default: false
		},
		// 最小的绘制点数量
		minDot: {
			type: Number,
			default: 4
		},
		maxDot: {
			type: Number,
			default: 9
		},
	},
	data() {
		return {
			// 九个点激活状态
			items: [
				{
					active: false
				},
				{
					active: false
				},
				{
					active: false
				},
				{
					active: false
				},
				{
					active: false
				},
				{
					active: false
				},
				{
					active: false
				},
				{
					active: false
				},
				{
					active: false
				},
			],
			//绘制区域的位置
			areaAxis: {
				x: 0,
				y: 0
			},
			// 九个点的坐标数据
			axisPoints: [],
			//点的触发半径
			activeRadius: 0,
			// 存储最终激活点的表现
			drawPoints: [],
			segmentNode: null,
			//存储线条数据
			lineSegment: [],
			// 是否处于绘制中
			isStart: false,



		}
	},
	mounted() {
		//获取点的触发半径（在css中.dot-outerde的宽高默认120rpx,如需调整大小也可以定义为变量）
		const resp = uni.getSystemInfoSync();
		this.activeRadius = resp.screenWidth / 750 * 120 / 2;
		//获取绘制区域的位置
		uni.createIntersectionObserver(this).relativeTo('.gesture-box').observe('.points', (res) => {
			this.areaAxis.x = res.boundingClientRect.left;
			this.areaAxis.y = res.boundingClientRect.top;

			//设置九个点的坐标
			let itemWidth = resp.screenWidth / 750 * 600 / 3;
			let itemRadius = itemWidth / 2;
			for (let i = 0; i < 9; i++) {
				let x = (i % 3) * itemWidth + itemRadius;
				let y = Math.floor(i / 3) * itemWidth + itemRadius;
				this.axisPoints.push({
					x: x,
					y: y
				});
			}
		})
	},
	methods: {
		//手指按下（开始绘制）
		touchstart(e) {
			//禁止绘制图案
			if (!this.isUse) {
				return;
			}
			
			// 每次手指按下初始化这个数据
			this.segmentNode = null;
			this.isStart = false;
			//如果已有绘制图案，进行清空
			if (this.drawPoints.length != 0) {
				this.clearDraw();

			}

			// 手指按下点坐标
			let startX = e.touches[0].pageX - this.areaAxis.x;
			let startY = e.touches[0].pageY - this.areaAxis.y;
			this.drawPoints = [];
			for (let i = 0; i < 9; i++) {
				// 图案上点的坐标
				let x = this.axisPoints[i].x;
				let y = this.axisPoints[i].y;
				//获取两个坐标点之间的距离
				let d = this.getPointerDistance(x, y, startX, startY);
				//接触第一个点
				if (d <= this.activeRadius) {
					this.drawPoints.push(i + 1);
					this.items[i].active = true;
					//记录线段节点
					this.setLineSegment(x, y);
					this.isStart = true;
				}
			}
		},
		//手指移动（连续绘制）
		touchmove(e) {
			//禁止绘制图案
			if (!this.isUse) {
				return;
			}
			// 未进入划动流程;
			if (!this.isStart) {
				return;
			}
			let newX = e.touches[0].pageX - this.areaAxis.x;
			let newY = e.touches[0].pageY - this.areaAxis.y;
			// 更新线段数据
			this.updateLineSegment(newX, newY);
			for (let i = 0; i < 9; i++) {
				// 图案上点的坐标
				let x = this.axisPoints[i].x;
				let y = this.axisPoints[i].y;
				//获取两个坐标点之间的距离
				let d = this.getPointerDistance(x, y, newX, newY);
				//判断是否接触某个点
				if (d <= this.activeRadius) {
					let val = i + 1;
					if (this.drawPoints.indexOf(val) > -1) {
						break;
					} else {
						this.drawPoints.push(val);
						this.items[i].active = true;
						// 设置当前线段数据
						this.setLineSegment(x, y);
						break;
					}
				}
			}
		},
		//手指抬起（绘制结束）
		touchend(e) {
			//禁止绘制图案
			if (!this.isUse) {
				return;
			}
			// 未进入划动流程
			if (!this.isStart) {
				return;
			}
			//判断绘制的点是否小于4个
			if (this.dotVerifyNum && this.drawPoints.length < this.minDot) {
				this.drawPoints = [];
				this.clearDraw();
				uni.hideToast();
				uni.showToast({
					icon: 'none',
					title: `请至少连接${this.minDot}个点`,
					duration: 2000,
					position: 'bottom'
				});
			}
			if (this.segmentNode && this.segmentNode.node) {
				this.lineSegment.pop();
			}
			this.segmentNode = null;
			this.$emit('change', this.drawPoints.join(''));
		},
		//闭合完整线段并绘制线段起点
		setLineSegment(x, y) {
			if (this.segmentNode != null) {
				// 两个点之间的距离为线段长度
				let d = this.getPointerDistance(x, y, this.segmentNode.sX, this.segmentNode.sY);
				// 两个点直接的倾斜关系
				let angle = this.getAngle(this.segmentNode.sX, this.segmentNode.sY, x, y);
				this.segmentNode.node.width = d;
				this.segmentNode.node.angle = angle;
			}
			let lineNode = this.createSegment(x, y);
			this.segmentNode = {
				node: lineNode,
				sX: x,
				sY: y
			};
		},
		//更新不完整线段的结束点
		updateLineSegment(x, y) {
			if (this.segmentNode != null) {
				let d = this.getPointerDistance(x, y, this.segmentNode.sX, this.segmentNode.sY);
				let angle = this.getAngle(this.segmentNode.sX, this.segmentNode.sY, x, y);
				this.segmentNode.node.width = d;
				this.segmentNode.node.angle = angle;
			}
		},
		// 创建新的线段基本数据
		createSegment(x, y) {
			let lineNode = {
				left: x,
				top: y - 2.5,
				width: 0,
				angle: 0
			}
			this.lineSegment.push(lineNode);
			return lineNode;
		},
		//获取两个坐标点之间的距离
		getPointerDistance(px1, py1, px2, py2) {
			let x = Math.abs(px1 - px2);
			let y = Math.abs(py1 - py2);
			let val = x * x + y * y;
			return Math.sqrt(val);
		},
		// 清空数据
		clearDraw() {
			this.items.forEach(item => {
				item.active = false;
			})
			this.lineSegment = [];
		},
		//获取两坐标点形成的直线与X轴夹角(顺时针)
		getAngle(px1, py1, px2, py2) {
			let rad = Math.atan2((py2 - py1), (px2 - px1));
			let angle = rad * (180 / Math.PI);
			return angle;
		},
	},

}
</script>

<style lang="scss">
.gesture-box {
	width: 600rpx;
	height: 600rpx;
	position: relative;
	margin: 0 auto;

	.points {
		width: 100%;
		height: 100%;
		position: relative;
		display: flex;
		flex-wrap: wrap;
		flex-direction: row;
		// display: grid;
		// grid-template-columns: 1fr 1fr 1fr;
		// grid-gap: 0rpx 0rpx;
		overflow: hidden;

		.item {
			width: 200rpx;
			height: 200rpx;
			display: flex;
			justify-content: center;
			align-items: center;

			.dot-outer {
				background-color: transparent;
				width: 120rpx;
				height: 120rpx;
				border-radius: 50%;
				display: flex;
				justify-content: center;
				align-items: center;
			}

			.dot {
				background-color: #dddddd;
				width: 40rpx;
				height: 40rpx;
				border-radius: 50%;
			}
		}

		.active {
			.dot-outer {
				background-color: #A6D3E0 !important;
			}

			.dot {
				background-color: #039CDF !important;
			}
		}
	}

	.line-segment {
		height: 5rpx;
		position: absolute;
		background-color: #0295d3;
		transform-origin: 0 50% 0;
	}
}
</style>
